package com.senbusi.shiro.utils;

import freemarker.template.utility.NullArgumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Set;

/**
 * 日期转换工具
 *
 * @author senbusi
 */
public class DateUtils {

    private static final Logger logger = LoggerFactory.getLogger(DateUtils.class);

    public static final int TIME_SECOND = 1000;

    public static final int TIME_MINUTE = 60 * TIME_SECOND;

    public static final int TIME_HOUR = 60 * TIME_MINUTE;

    public static final int TIME_DAY = 24 * TIME_HOUR;

    public enum Pattern {
        YEAR("yyyy"), //
        MONTH("MM"), //
        DAY("dd"), //
        HOUR("HH"), //
        MINUTE("mm"), //
        SECOND("ss"), //
        YEAR_MONTH("yyyy/MM"), //
        YEAR_MONTH_SHORT("yyyyMM"), //
        YEAR_MONTH_CN("yyyy-MM"), //
        DATE_CN("yyyy-MM-dd"), //
        DATE_EN("yyyy/MM/dd"), //
        DATE_SHORT("yyyyMMdd"), //
        TIME_FULL("HH:mm:ss"), //
        TIME_SHORT("HHmmss"), //
        TIME_HM("HH:mm"), //
        TIME_HM_S("HHmm"), //
        DATETIME_YMDHM_CN("yyyy-MM-dd HH:mm"), //
        DATETIME_SHORT("yyyyMMddHHmmss"), //
        DATETIME_FULL_SHORT("yyyyMMddHHmmssSSS"), //
        DATETIME_CN("yyyy-MM-dd HH:mm:ss"), //
        DATETIME_EN("yyyy/MM/dd HH:mm:ss");

        private final String value;

        private Pattern(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }
    }

    protected static final String[] WEEK_CN = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
    protected static final String[] WEEK_CN_SHORT = {"周日", "周一", "周二", "周三", "周四", "周五", "周六"};

    /**
     * 获日期取周中文名称
     *
     * @param date
     * @return
     */
    public static String getWeek(Date date) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int week_index = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (week_index < 0) {
            week_index = 0;
        }
        return WEEK_CN[week_index];
    }

    /**
     * 获工作是取周中文名称
     *
     * @param week
     * @return
     */
    public static String getWeek(final Integer week) {
        int index_week = 0;
        if (week != null && week >= 0 && week <= 6) {
            index_week = week;
        }
        return WEEK_CN[index_week];
    }

    /**
     * 获工作是取周中文名称
     *
     * @param week
     * @return
     */
    public static String getWeekShort(final Integer week) {
        int index_week = 0;
        if (week != null && week >= 0 && week <= 6) {
            index_week = week;
        }
        return WEEK_CN_SHORT[index_week];
    }


    /**
     * Return the current date
     *
     * @return － DATE<br>
     */
    public static Date currDateTime() {
        Calendar cal = Calendar.getInstance();
        return cal.getTime();
    }

    /**
     * Return the current date
     *
     * @return － DATE<br>
     */
    public static Date currDateTimeCN() {
        Date date = currDateTime();
        String dateStr = format(Pattern.DATETIME_CN, date);
        return parseDateTime(dateStr);
    }

    /**
     * Return the current date string
     *
     * @return － 产生的日期字符串<br>
     */
    public static String getCurrentDateStr() {
        Calendar cal = Calendar.getInstance();
        Date currDate = cal.getTime();
        return format(currDate);
    }

    /**
     * Return the current date in the specified format
     *
     * @return
     */
    public static String getCurrentDateTimeStr() {
        Calendar cal = Calendar.getInstance();
        Date currDate = cal.getTime();

        return formatTime(currDate);
    }

    public static Date parse(final Pattern pattern, String dateValue) {
        if (dateValue == null) {
            return null;
        }
        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern.getValue());

        try {
            return dateFormat.parse(dateValue);
        } catch (ParseException e) {
            logger.error(DateUtils.class.getSimpleName(), e);
        }

        return null;

    }

    /**
     * Parse a string and return a date timestamp
     *
     * @param timestamp
     * @return
     * @throws Exception
     */
    public static Date parse(Long timestamp) {
        return new Date(timestamp);
    }

    /**
     * Parse a string and return a date value
     *
     * @param dateValue
     * @return
     * @throws Exception
     */
    public static Date parseDate(String dateValue) {
        return parse(Pattern.DATE_CN, dateValue);
    }

    /**
     * Parse a strign and return a datetime value
     *
     * @param dateValue
     * @return
     */
    public static Date parseDateTime(String dateValue) {
        return parse(Pattern.DATETIME_CN, dateValue);
    }

    /**
     * String转换Date("yyyy-MM-dd HH:mm")
     *
     * @param dateValue
     * @return
     */
    public static Date parseDateTimeHM(String dateValue) {
        return parse(Pattern.DATETIME_YMDHM_CN, dateValue);
    }

    /**
     * 将Timestamp类型的日期转换为系统参数定义的格式的字符串。
     *
     * @param datetime 需要转换的日期。
     * @return 转换后符合给定格式的日期字符串
     */
    public static String format(Date datetime) {
        return format(Pattern.DATE_CN, datetime);
    }

    /**
     * 将Timestamp类型的日期转换为系统参数定义的格式的字符串。
     *
     * @param datetime 需要转换的日期。
     * @return 转换后符合给定格式的日期字符串
     */
    public static String formatTime(Date datetime) {
        return format(Pattern.DATETIME_CN, datetime);
    }

    public static String formatTimeYMDHM(Date dateTime) {
        return format(Pattern.DATETIME_YMDHM_CN, dateTime);
    }

    /**
     * 将Date类型的日期转换为系统参数定义的格式的字符串。
     *
     * @param pattern
     * @param datetime
     * @return
     */
    public static String format(final Pattern pattern, Date datetime) {
        if (datetime == null)
            return null;
        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern.getValue());
        return dateFormat.format(datetime);
    }

    /**
     * 取得指定日期N天后的日期
     *
     * @param date
     * @param days
     * @return
     */
    public static Date addDays(Date date, int days) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);

        cal.add(Calendar.DAY_OF_MONTH, days);

        return cal.getTime();
    }

    /**
     * 计算两个日期之间相差的天数
     *
     * @param date1
     * @param date2
     * @return
     */
    public static int daysBetween(Date date1, Date date2) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date1);
        long time1 = cal.getTimeInMillis();
        cal.setTime(date2);
        long time2 = cal.getTimeInMillis();
        long betweenDays = (time2 - time1) / (1000 * 3600 * 24);

        return Integer.parseInt(String.valueOf(betweenDays));
    }

    /**
     * 计算当前日期相对于"1977-12-01"的天数
     *
     * @param date
     * @return
     */
    public static long getRelativeDays(Date date) {
        Date relativeDate = DateUtils.parse(Pattern.DATE_CN, "1977-12-01");

        return DateUtils.daysBetween(relativeDate, date);
    }

    public static Date getDateBeforTwelveMonth() {
        String date = "";
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(currDateTime());
        int year = calendar.get(Calendar.YEAR) - 1;
        int month = calendar.get(Calendar.MONTH) + 1;
        if (month > 9) {
            date = String.valueOf(year) + "-" + month + "-01";
        } else {
            date = String.valueOf(year) + "-0" + month + "-01";
        }

        return parseDate(date);
    }

    /**
     * 传入时间字符串,加一天后返回Date
     *
     * @param date 时间 格式 YYYY-MM-DD
     * @return
     */
    public static Date addDate(String date) {
        if (date == null) {
            return null;
        }

        Date tempDate = parse(Pattern.DATE_CN, date);
        String year = format(Pattern.YEAR, tempDate);
        String month = format(Pattern.MONTH, tempDate);
        String day = format(Pattern.DAY, tempDate);

        GregorianCalendar calendar = new GregorianCalendar(Integer.parseInt(year), Integer.parseInt(month) - 1, Integer.parseInt(day));

        calendar.add(GregorianCalendar.DATE, 1);
        return calendar.getTime();
    }

    public static Integer getOfType(Date date, int type) {
        if (date == null) {
            return null;
        }
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        int t = Calendar.YEAR;
        switch (type) {
            case Calendar.MONTH:
                t = Calendar.MONTH;
                return c.get(t) + 1;
            case Calendar.DATE:
                t = Calendar.DATE;
                break;
            case Calendar.HOUR:
                t = Calendar.HOUR;
                break;
            case Calendar.MINUTE:
                t = Calendar.MINUTE;
                break;
            case Calendar.SECOND:
                t = Calendar.SECOND;
                break;
            case Calendar.DAY_OF_WEEK:
                t = Calendar.DAY_OF_WEEK;
                break;
            case Calendar.DAY_OF_WEEK_IN_MONTH:
                t = Calendar.DAY_OF_WEEK_IN_MONTH;
                break;
            default:
                break;
        }
        return c.get(t);
    }

    public static Date getFirstDayOfMouth(Date date) {
        if (date == null) {
            return null;
        }
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.DAY_OF_MONTH, 1);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime();
    }

    /**
     * 时间去掉时分秒（yyy-MM-dd 00:00:00.000）
     *
     * @param dateTime
     * @return
     */
    public static Date clearTime(Date dateTime) {
        if (dateTime == null) {
            return null;
        }
        Calendar c = Calendar.getInstance();
        c.setTime(dateTime);
        c.set(Calendar.HOUR_OF_DAY, 0);
        c.set(Calendar.MINUTE, 0);
        c.set(Calendar.SECOND, 0);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime();
    }

    /**
     * 时间去掉毫秒（yyy-MM-dd HH:mm:ss.000）
     *
     * @param dateTime
     * @return
     */
    public static Date clearTimeMS(Date dateTime) {
        if (dateTime == null) {
            return null;
        }
        Calendar c = Calendar.getInstance();
        c.setTime(dateTime);
        c.set(Calendar.MILLISECOND, 0);
        return c.getTime();
    }

    /**
     * 日期最后一刻（yyy-MM-dd 23:59:59.999）
     *
     * @param dateTime
     * @return
     */
    public static Date endTime(Date dateTime) {
        if (dateTime == null) {
            return null;
        }
        Calendar c = Calendar.getInstance();
        c.setTime(dateTime);
        c.set(Calendar.HOUR_OF_DAY, 23);
        c.set(Calendar.MINUTE, 59);
        c.set(Calendar.SECOND, 59);
        c.set(Calendar.MILLISECOND, 999);
        return c.getTime();
    }

    public static Date addHours(Date date, int hours) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.HOUR_OF_DAY, hours);
        return cal.getTime();
    }

    public static Date addMinutes(Date date, int minutes) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.add(Calendar.MINUTE, minutes);
        return cal.getTime();
    }

    /**
     * 计算两个日期之间的工作日数
     *
     * @param start   开始日期
     * @param end     结束日期
     * @param workday 适用工作日（1周日2周一3周二4周三5周四6周五7周六）
     * @return 工作日数
     */
    public static int workdays(Date start, Date end, Set<Integer> workday) {
        if (start == null || end == null) {
            throw new NullArgumentException("start ro end date is require!");
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(start);
        int count = 0;
        while (cal.getTime().before(addDays(end, 1))) {
            if (workday.contains(getOfType(cal.getTime(), Calendar.DAY_OF_WEEK))) {
                count++;
            }
            cal.add(Calendar.DAY_OF_MONTH, 1);
        }
        return count;
    }

    /**
     * 计算两个日期之间的工作日数
     *
     * @param start   开始日期
     * @param end     结束日期
     * @param workday 适用工作日（1周日2周一3周二4周三5周四6周五7周六）
     * @return 工作日数
     */
    public static int workdays(Date start, Date end, String workday) {
        if (start == null || end == null) {
            throw new NullArgumentException("start ro end date is require!");
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(start);
        int count = 0;
        while (cal.getTime().before(addDays(end, 1))) {
            Integer tmp = getOfType(cal.getTime(), Calendar.DAY_OF_WEEK);
            if (tmp != null && workday.contains(tmp.toString())) {
                count++;
            }
            cal.add(Calendar.DAY_OF_MONTH, 1);
        }
        return count;
    }

    /**
     * 1234567转中文
     *
     * @param workday
     * @param separator 分割符
     * @return
     */
    public static String convertWorkdays(String workday, String separator) {
        return convertWorkdays(workday, separator, "zh_cn_short");
    }

    /**
     * 1234567转中文
     *
     * @param workday
     * @param separator 分割符
     * @param nameType  中文（全、简写）
     * @return
     */
    public static String convertWorkdays(String workday, String separator, String nameType) {
//        if (StringUtils.isBlank(workday)) {
//            return "-";
//        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < workday.length(); i++) {
            if (sb.length() > 0) {
                sb.append(separator);
            }
            String day = String.valueOf(workday.charAt(i));
//            if (StringUtils.isNumeric(day)) {
//                if ("zh_cn".equals(nameType)) {
//                    sb.append(DateUtils.getWeek(Integer.valueOf(day)));
//                } else if ("zh_cn_short".equals(nameType)) {
//                    sb.append(DateUtils.getWeekShort(Integer.valueOf(day)));
//                }
//            }
        }
        return sb.toString();
    }

    /**
     * 时间偏移
     *
     * @param date      时间
     * @param offsetDay 天数
     * @param offsetMin 分钟数
     * @return 跨越天数（以0点为准）
     */
    public static int calcOffset(Date date, int offsetDay, int offsetMin) {
//        Date date = DateUtils.parse(Pattern.TIME_HM_S, hm);
        if (date == null) return 0;
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        Date d0 = cal.getTime();
        Date d1 = addDays(date, offsetDay);
        Date d2 = addMinutes(d1, offsetMin);
        Date d3 = d1.after(d2) ? d1 : d2;
        d3 = clearTime(d3);
        int n = 0;
        do {
            d3 = addDays(d3, -1);
            if (d3.after(d0) && !d2.before(d3)) {
                n++;
            }
        } while (d3.after(d0));
        return n;
    }

    /**
     * 分钟偏移
     *
     * @param date      时间
     * @param offsetDay 天数
     * @param offsetMin 分钟数
     * @return
     */
    public static Date moveMinute(Date date, int offsetDay, int offsetMin) {
        if (date == null) return null;
        Date d1 = addDays(date, offsetDay);
        Date d2 = addMinutes(d1, offsetMin);
        return addDays(d2, -offsetDay);
    }

    /**
     * 分钟偏移
     *
     * @param hm        时分
     * @param offsetDay 天数
     * @param offsetMin 分钟数
     * @return
     */
    public static String moveMinute(String hm, int offsetDay, int offsetMin) {
        Date date = DateUtils.parse(Pattern.TIME_HM_S, hm);
        if (date == null) return null;
//        logger.info("{}|{}|{}", formatTime(d1), formatTime(d2), formatTime(d3));
        return format(Pattern.TIME_HM_S, moveMinute(date, offsetDay, offsetMin));
    }


    /**
     * 两个日期取小值
     *
     * @param date1
     * @param date2
     * @return
     */
    public static Date min(Date date1, Date date2) {
        if (date1 == null) return date2;
        else if (date2 == null) return date1;
        else if (date1.after(date2)) return date2;
        return date1;
    }

    /**
     * 两个日期取大值
     *
     * @param date1
     * @param date2
     * @return
     */
    public static Date max(Date date1, Date date2) {
        if (date1 == null) return date2;
        else if (date2 == null) return date1;
        else if (date1.before(date2)) return date2;
        return date1;
    }

    /**
     * <p>Title: exchangeDate</p>
     * <p>Description: </p>
     *
     * @param nyr 取年月日
     * @param sfm 取时分
     * @return
     */
    public static Date exchangeDate(Date nyr, Date sfm) {
        Date date = new Date();
        try {
            String sfmStr = format(Pattern.TIME_HM, sfm);
            String nyrStr = format(nyr);
            date = parse(Pattern.DATETIME_YMDHM_CN, nyrStr + " " + sfmStr);
        } catch (Exception e) {
        }
        return date;
    }

    public static String exchangeString(Date nyr, Date sfm) {
        Date date = new Date();
        try {
            String sfmStr = format(Pattern.TIME_HM, sfm);
            String nyrStr = format(nyr);
            return nyrStr + " " + sfmStr;
        } catch (Exception e) {
        }
        return formatTimeYMDHM(date);
    }

}